package engine.graphics.misc;
/* License
 * 
 * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  
 *  * Redistribution of source code must retain the above copyright notice,
 *	this list of conditions and the following disclaimer.
 * 
 *  * Redistribution in binary form must reproduce the above copyright notice,
 *	this list of conditions and the following disclaimer in the
 *	documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *  
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *  
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility. 
 */

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * A custom bitmapped font read from a png file.
 * 
 * Font is a final class, so we are unable to subclass it. Therefore, this class
 * implements many of the same methods for compatibility purposes. However, to
 * render this font you need to call the drawChar, drawChars, drawString, and
 * drawSubstring methods from your paint method.
 */
public final class CustomFont {
	private int style;

	private int size;

	private int baseline;

	private int height;

	private int width;

	private Image image;

	/**
	 * Returns a soft font based on the specified resource name and the
	 * specified this.size and this.style. The resource name must refer to a png
	 * file. The this.size and this.style constants are defined on the Font
	 * class.
	 */
	public static CustomFont getFont(final String inName, final int inStyle, final int inSize) {
		Image i;
		String filename = inName;
		try {
			i = Image.createImage(filename);
		} catch (Throwable t) {
			t.printStackTrace();
			throw new IllegalArgumentException("Could not locate font: " + filename + " : " + t);
		}

		return new CustomFont(i, inSize, inStyle);
	}

	private CustomFont(final Image inImage, final int inStyle, final int inSize) {
		this.image = inImage;
		this.style = inStyle;
		this.size = inSize;

		try {
			this.height = this.image.getHeight();
			this.width = this.image.getWidth() / 128;
			this.baseline = calculateBaseline();
		} catch (Throwable t) {
			t.printStackTrace();
			throw new IllegalArgumentException("Specified font is invalid: " + t);
		}
	}

	private int calculateBaseline() {
		// get this.baseline: defaults to last row
		int result = this.height;
		int imageWidth = this.image.getWidth();
		int max = 0;
		int total;
		int[] row = new int[imageWidth];
		int background;

		// determine background color: assume it's at 0, 0
		this.image.getRGB(row, 0, 1, 0, 0, 1, 1);
		background = row[0];

		// here's the heuristic: find the row on the bottom
		// half of the this.image with the most non-background pixels
		for (int y = this.height / 2; y < this.height; y++) {
			total = 0;
			this.image.getRGB(row, 0, imageWidth, 0, y, imageWidth, 1);
			for (int x = 0; x < imageWidth; x++) {
				if (row[x] != background)
					total++;
			}
			if (total > max) {
				max = total;
				result = y;
			}
		}

		return result;
	}

	public int charsWidth(final char[] ch, final int offset, final int length) {
		// monospaced font makes this an easy calculation		
		return length * this.width;
	}

	public int charWidth(final char ch) {
		// we're monospaced so all are equal
		return this.width;
	}

	public int getBaselinePosition() {
		return this.baseline;
	}

	public int getHeight() {
		return this.height;
	}

	public int stringWidth(final String str) {
		return charsWidth(str.toCharArray(), 0, str.length());
	}

	public int substringWidth(final String str, final int offset, final int len) {
		return charsWidth(str.toCharArray(), offset, len);
	}

	public int getSize() {
		return this.size;
	}

	public int getStyle() {
		return this.style;
	}

	public boolean isBold() {
		return ((this.style & Font.STYLE_BOLD) != 0);
	}

	public boolean isItalic() {
		return ((this.style & Font.STYLE_ITALIC) != 0);
	}

	public boolean isPlain() {
		return (this.style == 0);
	}

	public boolean isUnderlined() {
		return ((this.style & Font.STYLE_UNDERLINED) != 0);
	}

	/**
	 * Paints the specified character at the specified coordinates on the
	 * Graphics instance using Graphics' anchoring constants.
	 */
	public void drawChar(final Graphics g, final char character, final int x, final int y, final int anchor) {
		int clipX = g.getClipX();
		int clipY = g.getClipY();
		int clipW = g.getClipWidth();
		int clipH = g.getClipHeight();

		drawCharInternal(g, character, x, y, anchor);

		g.setClip(clipX, clipY, clipW, clipH);
	}

	/**
	 * Paints the characters as specified.
	 */
	public void drawChars(final Graphics g, final char[] data, final int offset, final int length, final int x, final int y,
			final int anchor) {
		int nx = x;
		int ny = y;
		if ((anchor & Graphics.RIGHT) != 0) {
			nx -= charsWidth(data, offset, length);
		} else if ((anchor & Graphics.HCENTER) != 0) {
			nx -= (charsWidth(data, offset, length) / 2);
		}

		if ((anchor & Graphics.BOTTOM) != 0) {
			ny -= this.height;
		} else if ((anchor & Graphics.VCENTER) != 0) {
			ny -= this.height / 2;
		}

		int clipX = g.getClipX();
		int clipY = g.getClipY();
		int clipW = g.getClipWidth();
		int clipH = g.getClipHeight();

		char c;
		for (int i = 0; i < length; i++) {
			c = data[offset + i];
			drawCharInternal(g, c, nx, ny, Graphics.TOP | Graphics.LEFT);
			nx += this.width;
		}

		g.setClip(clipX, clipY, clipW, clipH);
	}

	/**
	 * Draws the actual characters without worrying about saving and restoring
	 * the existing clip region.
	 */
	private void drawCharInternal(final Graphics g, final char character, final int x, final int y, final int anchor) {
		if ((this.style & Font.STYLE_ITALIC) != 0) {
			// draw italicized: top half is shifted right
			g.setClip(x + 1, y, this.width, this.height / 2);
			g.drawImage(this.image, x - this.width * character + 1, y, anchor);
			g.setClip(x, y + this.height / 2, this.width, this.height / 2);
			g.drawImage(this.image, x - this.width * character, y, anchor);

			if ((this.style & Font.STYLE_BOLD) != 0) {
				g.setClip(x, y, this.width, this.height / 2);
				g.drawImage(this.image, x - this.width * character + 2, y, anchor);
				g.setClip(x, y + this.height / 2, this.width, this.height / 2);
				g.drawImage(this.image, x - this.width * character + 1, y, anchor);
			}
		} else {
			// draw normally
			g.setClip(x, y, this.width, this.height);
			g.drawImage(this.image, x - this.width * character, y, anchor);

			if ((this.style & Font.STYLE_BOLD) != 0) {
				g.drawImage(this.image, x - this.width * character + 1, y, anchor);
			}
		}

		if ((this.style & Font.STYLE_UNDERLINED) != 0) {
			g.drawLine(x, y + this.baseline + 2, x + this.width, y + this.baseline + 2);
		}
	}

	/**
	 * Paints the string as specified.
	 */
	public void drawString(final Graphics g, final String str, final int x, final int y, final int anchor) {
		drawChars(g, str.toCharArray(), 0, str.length(), x, y, anchor);
	}

	/**
	 * Paints the substring as specified.
	 */
	public void drawSubstring(final Graphics g, final String str, final int offset, final int len, final int x, final int y, final int anchor) {
		drawChars(g, str.toCharArray(), offset, len, x, y, anchor);
	}

}
